package com.amap.flutter.tmap.core;


import static com.tencent.tencentmap.mapsdk.maps.model.MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER;

import android.content.Context;
import android.graphics.Bitmap;
import android.location.Location;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;

import com.tencent.map.geolocation.TencentLocation;
import com.tencent.map.geolocation.TencentLocationListener;
import com.tencent.map.geolocation.TencentLocationManager;
import com.tencent.map.geolocation.TencentLocationRequest;
import com.tencent.tencentmap.mapsdk.maps.CameraUpdate;
import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory;
import com.tencent.tencentmap.mapsdk.maps.LocationSource;
import com.tencent.tencentmap.mapsdk.maps.MapView;
import com.tencent.tencentmap.mapsdk.maps.TencentMap;
import com.tencent.tencentmap.mapsdk.maps.TencentMapInitializer;
import com.tencent.tencentmap.mapsdk.maps.TextureMapView;
import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptor;
import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory;
import com.tencent.tencentmap.mapsdk.maps.model.CameraPosition;
import com.tencent.tencentmap.mapsdk.maps.model.LatLng;
import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds;
import com.tencent.tencentmap.mapsdk.maps.model.MapPoi;
import com.tencent.tencentmap.mapsdk.maps.model.MyLocationStyle;
import com.amap.flutter.tmap.MyMethodCallHandler;
import com.amap.flutter.map.utils.Const;
import com.amap.flutter.tmap.utils.ConvertUtil;
import com.amap.flutter.map.utils.LogUtil;
import com.tencent.tencentmap.mapsdk.maps.model.RestrictBoundsFitMode;

import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;

import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;

public class TencentMapController
        implements MyMethodCallHandler,
        TencentMapOptionsSink,
        TencentMap.OnMapLoadedCallback,
        TencentMap.OnMyLocationChangeListener,
        TencentMap.OnCameraChangeListener,
        TencentMap.OnMapClickListener,
        TencentMap.OnMapLongClickListener,
        TencentMap.OnMapPoiClickListener{
    private final MethodChannel methodChannel;
    private final TencentMap tencentMap;
    private final MapView mapView;
    private MethodChannel.Result mapReadyResult;

    private static final String CLASS_NAME = "TencentMapController";

    private boolean mapLoaded = false;

    private TencentLocationManager locationManager;
    private LocationSource.OnLocationChangedListener locationChangedListener;

    public TencentMapController(MethodChannel methodChannel, MapView mapView) {
        this.methodChannel = methodChannel;
        this.mapView = mapView;
        tencentMap = mapView.getMap();

        tencentMap.setOnMapLoadedCallback(this);
        tencentMap.setOnMyLocationChangeListener(this);
        tencentMap.setOnCameraChangeListener(this);
        tencentMap.setOnMapLongClickListener(this);
//        tencentMap.setOnMapClickListener(this);
//        tencentMap.setOnMapPoiClickListener(this);
        tencentMap.setPoisEnabled(true);
        tencentMap.getUiSettings().setAllGesturesEnabled(true);
        tencentMap.getUiSettings().setTiltGesturesEnabled(true);
        tencentMap.setOnMapClickListener(new TencentMap.OnMapClickListener() {
            @Override
            public void onMapClick(LatLng latLng) {
                Log.e("TAG", "onMapClick: " );
                if (null != methodChannel) {
                    final Map<String, Object> arguments = new HashMap<String, Object>(2);
                    arguments.put("latLng", ConvertUtil.latLngToList(latLng));
                    methodChannel.invokeMethod("map#onTap", arguments);
                    LogUtil.i(CLASS_NAME, "onMapClick===>" + arguments);
                }
            }
        });
        tencentMap.setLocationSource(new LocationSource() {
            @Override
            public void activate(OnLocationChangedListener onLocationChangedListener) {
                locationChangedListener = onLocationChangedListener;
                int err = locationManager.requestSingleFreshLocation(null, new TencentLocationListener() {
                    @Override
                    public void onLocationChanged(TencentLocation tencentLocation, int i, String s) {
                        if(i == TencentLocation.ERROR_OK && locationChangedListener != null){
                            Location location = new Location(tencentLocation.getProvider());
                            location.setLatitude(tencentLocation.getLatitude());
                            location.setLongitude(tencentLocation.getLongitude());
                            location.setAccuracy(tencentLocation.getAccuracy());
                            location.setBearing((float) tencentLocation.getDirection());

                            locationChangedListener.onLocationChanged(location);
                        }
                    }

                    @Override
                    public void onStatusUpdate(String s, int i, String s1) {

                    }
                }, Looper.myLooper());

            }

            @Override
            public void deactivate() {
//                locationManager.removeUpdates(locationChangedListener);
                locationManager = null;
                locationChangedListener=null;
            }
        });
        TencentLocationManager.setUserAgreePrivacy(true);
        initLocation();
    }
    private void initLocation(){
        //用于访问腾讯定位服务的类, 周期性向客户端提供位置更新
        locationManager = TencentLocationManager.getInstance(mapView.getContext());
        //设置坐标系
        locationManager.setCoordinateType(TencentLocationManager.COORDINATE_TYPE_GCJ02);
    }

    @Override
    public String[] getRegisterMethodIdArray() {
        return Const.METHOD_ID_LIST_FOR_MAP;
    }

    @Override
    public void doMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
        LogUtil.i(CLASS_NAME, "doMethodCall===>" + call.method);
        if (null == tencentMap) {
            LogUtil.w(CLASS_NAME, "onMethodCall tencentMap is null!!!");
            return;
        }
        switch (call.method) {
            case Const.METHOD_MAP_WAIT_FOR_MAP:
                if (mapLoaded) {
                    result.success(null);
                    return;
                }
                mapReadyResult = result;
                break;
            case Const.METHOD_MAP_SATELLITE_IMAGE_APPROVAL_NUMBER:
                if (null != tencentMap) {
//                    result.success(tencentMap.getSatelliteImageApprovalNumber());
                    result.success(null);
                }
                break;
            case Const.METHOD_MAP_CONTENT_APPROVAL_NUMBER:
                if (null != tencentMap) {
//                    result.success(tencentMap.getMapContentApprovalNumber());
                }
                break;
            case Const.METHOD_MAP_UPDATE:
                if (tencentMap != null) {
                    ConvertUtil.interpretTencentMapOptions(call.argument("options"), this);
                    result.success(ConvertUtil.cameraPositionToMap(getCameraPosition()));
                }
                break;
            case Const.METHOD_MAP_MOVE_CAMERA:
                if (null != tencentMap) {
                    final CameraUpdate cameraUpdate = ConvertUtil.toCameraUpdateTp(call.argument("cameraUpdate"));
                    final Object animatedObject = call.argument("animated");
                    final Object durationObject = call.argument("duration");
                    moveCamera(cameraUpdate, animatedObject, durationObject);
                }
                break;
            case Const.METHOD_MAP_SET_RENDER_FPS:
                if (null != tencentMap) {
//                    tencentMap.setRenderFps((Integer) call.argument("fps"));
                    result.success(null);
                }
                break;
            case Const.METHOD_MAP_TAKE_SNAPSHOT:
//                if (tencentMap != null) {
//                    final MethodChannel.Result _result = result;
//                    tencentMap.getMapScreenShot(new TencentMap.OnMapScreenShotListener() {
//                        @Override
//                        public void onMapScreenShot(Bitmap bitmap) {
//                            ByteArrayOutputStream stream = new ByteArrayOutputStream();
//                            bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
//                            byte[] byteArray = stream.toByteArray();
//                            bitmap.recycle();
//                            _result.success(byteArray);
//                        }
//
//                        @Override
//                        public void onMapScreenShot(Bitmap bitmap, int i) {
//                        }
//                    });
//                }
                break;
            case Const.METHOD_MAP_CLEAR_DISK:
                if (null != tencentMap) {
//                    tencentMap.removeCache();
                    result.success(null);
                }
                break;
            default:
                LogUtil.w(CLASS_NAME, "onMethodCall not find methodId:" + call.method);
                break;
        }
    }

    @Override
    public void onMapLoaded() {
        LogUtil.i(CLASS_NAME, "onMapLoaded==>");
        try {
            mapLoaded = true;
            if (null != mapReadyResult) {
                mapReadyResult.success(null);
                mapReadyResult = null;
            }
        } catch (Throwable e) {
            LogUtil.e(CLASS_NAME, "onMapLoaded", e);
        }
    }

    @Override
    public void setCamera(CameraPosition camera) {
        tencentMap.moveCamera(CameraUpdateFactory.newCameraPosition(camera));
    }

    @Override
    public void setMapType(int mapType) {
        tencentMap.setMapType(mapType);
    }

//    @Override
//    public void setCustomMapStyleOptions(CustomMapStyleOptions customMapStyleOptions) {
//        if (null != tencentMap) {
//            tencentMap.setCustomMapStyle(customMapStyleOptions);
//        }
//    }

    private boolean myLocationShowing = false;

    @Override
    public void setMyLocationStyle(MyLocationStyle myLocationStyle) {
        Log.e("haha", "setMyLocationStyle: "+myLocationStyle );
        if (null != tencentMap) {
//            myLocationShowing = myLocationStyle.isMyLocationShowing();
            myLocationShowing = true;
            tencentMap.setMyLocationEnabled(true);
            tencentMap.setMyLocationStyle(myLocationStyle);
        }
    }

    @Override
    public void setScreenAnchor(float x, float y) {
        tencentMap.setPointToCenter(Float.valueOf(mapView.getWidth() * x).intValue(), Float.valueOf(mapView.getHeight() * y).intValue());
    }

    @Override
    public void setMinZoomLevel(float minZoomLevel) {
        tencentMap.setMinZoomLevel(Float.valueOf(minZoomLevel).intValue());
    }

    @Override
    public void setMaxZoomLevel(float maxZoomLevel) {
        tencentMap.setMaxZoomLevel(Float.valueOf(maxZoomLevel).intValue());
    }

    @Override
    public void setLatLngBounds(LatLngBounds latLngBounds) {
        tencentMap.setRestrictBounds(latLngBounds, RestrictBoundsFitMode.FIT_WIDTH);
    }

    @Override
    public void setTrafficEnabled(boolean trafficEnabled) {
        tencentMap.setTrafficEnabled(trafficEnabled);
    }

    @Override
    public void setTouchPoiEnabled(boolean touchPoiEnabled) {
        tencentMap.setPoisEnabled(touchPoiEnabled);
    }

    @Override
    public void setBuildingsEnabled(boolean buildingsEnabled) {
        tencentMap.showBuilding(buildingsEnabled);
    }

    @Override
    public void setLabelsEnabled(boolean labelsEnabled) {
//        tencentMap.showMapText(labelsEnabled);
    }

    @Override
    public void setCompassEnabled(boolean compassEnabled) {
        tencentMap.getUiSettings().setCompassEnabled(compassEnabled);
    }

    @Override
    public void setScaleEnabled(boolean scaleEnabled) {
        tencentMap.getUiSettings().setScaleViewEnabled(scaleEnabled);
    }

    @Override
    public void setZoomGesturesEnabled(boolean zoomGesturesEnabled) {
        tencentMap.getUiSettings().setZoomGesturesEnabled(zoomGesturesEnabled);
    }

    @Override
    public void setScrollGesturesEnabled(boolean scrollGesturesEnabled) {
        tencentMap.getUiSettings().setScrollGesturesEnabled(scrollGesturesEnabled);
    }

    @Override
    public void setRotateGesturesEnabled(boolean rotateGesturesEnabled) {
        tencentMap.getUiSettings().setRotateGesturesEnabled(rotateGesturesEnabled);
    }

    @Override
    public void setTiltGesturesEnabled(boolean tiltGesturesEnabled) {
        tencentMap.getUiSettings().setTiltGesturesEnabled(tiltGesturesEnabled);
    }

    @Override
    public void setInitialMarkers(Object initialMarkers) {

    }

    @Override
    public void setInitialPolylines(Object initialPolylines) {

    }

    @Override
    public void setInitialPolygons(Object initialPolygons) {

    }

    private CameraPosition getCameraPosition() {
        if (null != tencentMap) {
            return tencentMap.getCameraPosition();
        }
        return null;
    }

    @Override
    public void onMyLocationChange(Location location) {
        if (null != methodChannel && myLocationShowing) {
            final Map<String, Object> arguments = new HashMap<String, Object>(2);
            arguments.put("location", ConvertUtil.location2Map(location));
            methodChannel.invokeMethod("location#changed", arguments);
            LogUtil.i(CLASS_NAME, "onMyLocationChange===>" + arguments);
        }
    }

    @Override
    public void onCameraChange(CameraPosition cameraPosition) {
        if (null != methodChannel) {
            final Map<String, Object> arguments = new HashMap<String, Object>(2);
            arguments.put("position", ConvertUtil.cameraPositionToMap(cameraPosition));
            methodChannel.invokeMethod("camera#onMove", arguments);
            LogUtil.i(CLASS_NAME, "onCameraChange===>" + arguments);
        }
    }

    @Override
    public void onCameraChangeFinished(CameraPosition cameraPosition) {
        if (null != methodChannel) {
            final Map<String, Object> arguments = new HashMap<String, Object>(2);
            arguments.put("position", ConvertUtil.cameraPositionToMap(cameraPosition));
            methodChannel.invokeMethod("camera#onMoveEnd", arguments);
            LogUtil.i(CLASS_NAME, "onCameraChangeFinish===>" + arguments);
        }
    }
    @Override
    public void onMapClick(LatLng latLng) {
        LogUtil.i(CLASS_NAME, "onMapClick==>");

        if (null != methodChannel) {
            final Map<String, Object> arguments = new HashMap<String, Object>(2);
            arguments.put("latLng", ConvertUtil.latLngToList(latLng));
            methodChannel.invokeMethod("map#onTap", arguments);
            LogUtil.i(CLASS_NAME, "onMapClick===>" + arguments);
        }
    }

    @Override
    public void onMapLongClick(LatLng latLng) {
        LogUtil.i(CLASS_NAME, "onMapLongClick==>");

        if (null != methodChannel) {
            final Map<String, Object> arguments = new HashMap<String, Object>(2);
            arguments.put("latLng", ConvertUtil.latLngToList(latLng));
            methodChannel.invokeMethod("map#onLongPress", arguments);
            LogUtil.i(CLASS_NAME, "onMapLongClick===>" + arguments);
        }
    }

    private void moveCamera(CameraUpdate cameraUpdate, Object animatedObject, Object durationObject) {
        boolean animated = false;
        long duration = 250;
        if (null != animatedObject) {
            animated = (Boolean) animatedObject;
        }
        if (null != durationObject) {
            duration = ((Number) durationObject).intValue();
        }
        if (null != tencentMap) {
            if (animated) {
                tencentMap.animateCamera(cameraUpdate, duration, null);
            } else {
                tencentMap.moveCamera(cameraUpdate);
            }
        }
    }

    @Override
    public void onClicked(MapPoi mapPoi) {
        LogUtil.i(CLASS_NAME, "onClicked==>");

        if (null != methodChannel) {
            final Map<String, Object> arguments = new HashMap<String, Object>(2);
            arguments.put("poi", ConvertUtil.poiToMap(mapPoi));
            methodChannel.invokeMethod("map#onPoiTouched", arguments);
            LogUtil.i(CLASS_NAME, "onPOIClick===>" + arguments);
        }
    }
}